// ValidationAspects
// Written by Mike Saunders
// Released under Microsoft Public License (Ms-PL)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using ValidationAspects.Core;
using ValidationAspects.Sdk;

namespace ValidationAspects
{
	public static class MethodValidationRegistry
	{
		private static readonly ValidationRegistry<MethodBase> registry = new ValidationRegistry<MethodBase>(m => m.DeclaringType,
		                                                                                                     m =>
		                                                                                                     ((ValidatorAttribute[])m.GetCustomAttributes(typeof(ValidatorAttribute), false)).Select(
		                                                                                                     	a => a.Factory));

		private static void EnforceCanValidate(MethodBase methodBase)
		{
			if (!CanValidate(methodBase))
				throw new ValidationException(string.Format("Object Validation cannot be applied to [method:{0}] [type:{1}]. Method is static or abstract.", methodBase, methodBase.DeclaringType));
		}

		public static bool CanValidate(this MethodBase methodBase)
		{
			Enforce.IsNotNull(methodBase, "methodBase");
			return !methodBase.IsStatic && !methodBase.IsAbstract;
		}

		public static void ClearCache()
		{
			registry.ClearCache();
		}

		public static void RevertValidation(this MethodBase methodBase)
		{
			EnforceCanValidate(methodBase);
			registry.RevertValidation(methodBase);
		}

		public static void ReplaceValidation(this MethodBase methodBase, IEnumerable<IValidatorFactory> factories)
		{
			EnforceCanValidate(methodBase);
			registry.ReplaceValidation(methodBase, factories);
		}

		public static void ReplaceValidation<TValue>(this MethodBase methodBase, Action<TValue, IValidationContext> validateAction)
		{
			EnforceCanValidate(methodBase);
			registry.ReplaceValidation(methodBase, validateAction);
		}

		public static void AddValidation(this MethodBase methodBase, IEnumerable<IValidatorFactory> factories)
		{
			EnforceCanValidate(methodBase);
			registry.AddValidation(methodBase, factories);
		}

		public static void AddValidation<TValue>(this MethodBase methodBase, Action<TValue, IValidationContext> validateAction)
		{
			EnforceCanValidate(methodBase);
			registry.AddValidation(methodBase, validateAction);
		}

		public static IValidator ResolveValidator(this MethodBase methodBase)
		{
			EnforceCanValidate(methodBase);

			try
			{
				return registry.ResolveValidator(methodBase);
			}
			catch (ValidationException e)
			{
				throw new ValidationException(string.Format("Failed to construct validator for [type:{0}] [method:{1}].", methodBase.DeclaringType, methodBase.Name), e);
			}
		}
	}
}